#ifndef _GAME_SHARED_H_
#define _GAME_SHARED_H_
#include "game_platform.h"
// clang-format off
static v3 DebugColorTable[] = {
    {1, 0, 0},      
    {0, 1, 0},      
    {0, 0, 1},      
    {1, 1, 0},      
    {0, 1, 1},      
    {1, 0.5, 0},    
    {1, 0, 0.5},    
    {0, 1, 0.5},    
    {0.5, 0, 1},    
    {0, 0.5, 1},    
    {0.5, 0.5, 1},  
};
// clang-format on
struct Int32_from_Z {
    int32 value;
    char *NewAt;
};
inline int32 Int32FromZInternal(const char **AtInit) {
    int32 Result = {};
    const char *At = *AtInit;
    while ((*At >= '0') && (*At <= '9')) {
        Result *= 10;
        Result += (*At - '0');
        ++At;
    }
    *AtInit = At;
    return Result;
}
inline int32 Int32FromZ(const char *At) {  //
    int32 Result = Int32FromZInternal(&At);
    return Result;
}
struct format_dest {
    uintptr_t Size;
    char *At;
};

inline void OutChar(format_dest *Dest, const char Value) {
    if (Dest->Size) {
        --Dest->Size;
        *Dest->At++ = Value;
    }
}

inline void OutChars(format_dest *Dest, const char *Value) {
    while (*Value) {
        OutChar(Dest, *Value++);
    }
}
#define ReadVarArgUnsignedInteger(Length, ArgList) \
    ((Length) == 8) ? va_arg(ArgList, uint64) : (uint64)va_arg(ArgList, uint32)

#define ReadVarArgSignedInteger(Length, ArgList) \
    ((Length) == 8) ? va_arg(ArgList, int64) : (int64)va_arg(ArgList, int32)

#define ReadVarArgFloat(Length, ArgList) \
    ((Length) == 8) ? va_arg(ArgList, real64) : (real64)va_arg(ArgList, real64)
#if 0
inline uint64 ReadVarArgUnsignedInteger(uint32 Length, va_list *ArgList) {
    uint64 Result = 0;
    switch (Length) {
        case 1:
        case 2:
        case 4: {
            Result = va_arg(*ArgList, uint32);
        } break;
        case 8: {
            Result = va_arg(*ArgList, uint64);
        } break;
    }
    return Result;
}

inline int64 ReadVarArgSignedInteger(uint32 Length, va_list *ArgList) {
    uint64 Temp = ReadVarArgUnsignedInteger(Length, ArgList);
    int64 Result = *(int64 *)&Temp;
    return (Result);
}
inline real64 ReadVarArgFloat(uint32 Length, va_list *ArgList) {
    real64 Result = 0;
    switch (Length) {
        case 4: {
            // Result = va_arg(*ArgList, real32);  // UB
            Result = (real64)va_arg(*ArgList, real64);
        } break;
        case 8: {
            Result = va_arg(*ArgList, real64);
        } break;
    }
    return Result;
}
#endif
inline void Uint64ToASCII(format_dest *Dest, uint64 Value, uint32 Base, char *Digits) {
    Assert(Base != 0);
    char *Start = Dest->At;
    do {
        uint64 DigitIndex = (Value % Base);
        char Digit = Digits[DigitIndex];
        OutChar(Dest, Digit);
        Value /= Base;
    } while (Value != 0);
    char *End = Dest->At;
    while (Start < End) {
        --End;
        char Temp = *End;
        *End = *Start;
        *Start = Temp;
        ++Start;
    }
}
static char DecChars[] = "0123456789";
static char LowerHexChars[] = "0123456789abcdef";
static char UpperHexChars[] = "0123456789ABCDEF";

inline void Real64ToASCII(format_dest *Dest, real64 Value, uint32 Precision) {
    if (Value < 0) {
        OutChar(Dest, '-');
        Value = -Value;
    }
    uint64 IntegerPart = (uint64)Value;
    Value -= (real64)IntegerPart;
    Uint64ToASCII(Dest, IntegerPart, 10, DecChars);
    OutChar(Dest, '.');
    for (uint32 PrecisionIndex = 0; PrecisionIndex < Precision; ++PrecisionIndex) {
        Value *= 10.0f;
        uint32 Integer = (uint32)Value;
        Value -= (real32)Integer;
        OutChar(Dest, DecChars[Integer]);
    }
}
internal uintptr_t FormatStringList(uintptr_t DestSize, char *DestInit, const char *Format,
                                    va_list ArgList) {
    format_dest Dest = {DestSize, DestInit};
    if (Dest.Size) {
        const char *At = Format;
        while (At[0]) {
            if (*At == '%') {
                ++At;
                bool32 ForceSign = false;
                bool32 PadWidthZero = false;
                (void)PadWidthZero;
                bool32 LeftJustify = false;
                (void)LeftJustify;
                bool32 PositiveSignIsBlank = false;
                bool32 AnnotateIfNotZero = false;
                bool32 Parsing = true;
                while (Parsing) {
                    switch (*At) {
                        case '+': {
                            ForceSign = true;
                        } break;
                        case '0': {
                            PadWidthZero = true;
                        } break;
                        case '-': {
                            LeftJustify = true;
                        } break;
                        case ' ': {
                            PositiveSignIsBlank = true;
                        } break;
                        case '#': {
                            AnnotateIfNotZero = true;
                        } break;
                        default: {
                            Parsing = false;
                        } break;
                    }
                    if (Parsing) {
                        ++At;
                    }
                }
                // 处理宽度
                bool32 WidthSpecified = false;
                (void)WidthSpecified;
                uint32 Width = 0;
                (void)Width;
                if (*At == '*') {
                    Width = va_arg(ArgList, int);
                    WidthSpecified = true;
                    ++At;
                } else if ((*At >= '0') && (*At <= '9')) {
                    Width = Int32FromZInternal(&At);
                    WidthSpecified = true;
                }
                // 处理精度
                bool32 PrecisionSpecified = false;
                (void)PrecisionSpecified;
                uint32 Precision = 0;
                (void)Precision;
                if (*At == '.') {
                    ++At;
                    if (*At == '*') {
                        Precision = va_arg(ArgList, int);
                        PrecisionSpecified = true;
                        ++At;
                    } else if ((*At >= '0') && (*At <= '9')) {
                        Precision = Int32FromZInternal(&At);
                        PrecisionSpecified = true;
                    } else {
                        Assert(!"Malformed precision specifier!");
                    }
                }
                if (!PrecisionSpecified) {
                    Precision = 6;
                }
                // 处理长度
                uint32 IntegerLength = 4;
                uint32 FloatLength = 4;

                if ((At[0] == 'h') && (At[1] == 'h')) {
                    At += 2;
                } else if ((At[0] == 'l') && (At[1] == 'l')) {
                    At += 2;
                } else if (At[0] == 'h') {
                    ++At;
                } else if (At[0] == 'l') {
                    ++At;

                } else if (At[0] == 'j') {
                    ++At;

                } else if (At[0] == 'z') {
                    ++At;

                } else if (At[0] == 't') {
                    ++At;

                } else if (At[0] == 'L') {
                    ++At;
                }
                char TempBuffer[64];
                char *Temp = TempBuffer;
                format_dest TempDest = {ArrayCount(TempBuffer), Temp};
                const char *Prefix = "";
                bool32 IsFloat = false;
                switch (*At) {
                    case 'd':
                    case 'i': {
                        int64 Value = ReadVarArgSignedInteger(IntegerLength, ArgList);
                        bool32 WasNegative = (Value < 0);
                        if (WasNegative) {
                            Value = -Value;
                        }
                        Uint64ToASCII(&TempDest, (uint64)Value, 10, DecChars);
                        if (WasNegative) {
                            Prefix = "-";
                        } else if (ForceSign) {
                            Assert(!PositiveSignIsBlank);
                            Prefix = "+";
                        } else if (PositiveSignIsBlank) {
                            Prefix = " ";
                        }
                    } break;
                    case 'u': {
                        int64 Value = ReadVarArgSignedInteger(IntegerLength, ArgList);
                        Uint64ToASCII(&TempDest, (uint64)Value, 10, DecChars);

                    } break;
                    case 'o': {
                        int64 Value = ReadVarArgSignedInteger(IntegerLength, ArgList);
                        Uint64ToASCII(&TempDest, (uint64)Value, 8, DecChars);
                        if (AnnotateIfNotZero && (Value != 0)) {
                            Prefix = "0";
                        }
                    } break;
                    case 'x': {
                        int64 Value = ReadVarArgSignedInteger(IntegerLength, ArgList);
                        Uint64ToASCII(&TempDest, (uint64)Value, 16, LowerHexChars);
                        if (AnnotateIfNotZero && (Value != 0)) {
                            Prefix = "0x";
                        }
                    } break;
                    case 'X': {
                        int64 Value = ReadVarArgSignedInteger(IntegerLength, ArgList);
                        Uint64ToASCII(&TempDest, (uint64)Value, 16, UpperHexChars);
                        if (AnnotateIfNotZero && (Value != 0)) {
                            Prefix = "0X";
                        }

                    } break;
                    case 'f':
                    case 'F':
                    case 'e':
                    case 'E':
                    case 'g':
                    case 'G':
                    case 'a':
                    case 'A': {
                        real64 Value = ReadVarArgFloat(FloatLength, ArgList);
                        Real64ToASCII(&TempDest, Value, Precision);
                        IsFloat = true;

                    } break;
                    case 'c': {
                        int Value = va_arg(ArgList, int);
                        OutChar(&TempDest, (char)Value);

                    } break;
                    case 's': {
                        char *String = va_arg(ArgList, char *);

                        Temp = String;
                        if (PrecisionSpecified) {
                            TempDest.Size = 0;
                            for (char *Scan = String; *Scan && (TempDest.Size < Precision);
                                 ++Scan) {
                                ++TempDest.Size;
                            }
                            TempDest.Size = StringLength(String);
                        } else {
                            TempDest.Size = StringLength(String);
                        }
                        TempDest.At = String + TempDest.Size;

                    } break;
                    case 'p': {
                        void *Value = va_arg(ArgList, void *);
                        Uint64ToASCII(&TempDest, *(uintptr_t *)&Value, 16, LowerHexChars);

                    } break;

                    case 'n': {
                        int *TabDest = va_arg(ArgList, int *);
                        *TabDest = (int)(Dest.At - DestInit);
                    } break;
                    case '%': {
                        OutChar(&Dest, '%');
                    } break;
                    default: {
                        Assert(!"Unrecognized format specifier!")
                    }
                }
                // PadWidthZero;
                // LeftJustify;
                if (TempDest.At - Temp) {
                    intptr_t UsePrecision = Precision;
                    if (IsFloat || !PrecisionSpecified) {
                        UsePrecision = (TempDest.At - Temp);
                    }
                    intptr_t PrefixLength = StringLength(Prefix);
                    intptr_t UseWidth = Width;
                    intptr_t ComputedWidth = UsePrecision + PrefixLength;

                    if (UseWidth < ComputedWidth) {
                        UseWidth = ComputedWidth;
                    }
                    if (PadWidthZero) {
                        Assert(!LeftJustify);
                        LeftJustify = false;
                    }
                    if (!LeftJustify) {
                        while (UseWidth > UsePrecision) {
                            OutChar(&Dest, PadWidthZero ? '0' : ' ');
                            --UseWidth;
                        }
                    }
                    for (const char *Pre = Prefix; *Pre && UseWidth; ++Pre) {
                        OutChar(&Dest, *Pre);
                        --UseWidth;
                    }
                    if (UsePrecision > UseWidth) {
                        UsePrecision = UseWidth;
                    }
                    while (UsePrecision > (TempDest.At - Temp)) {
                        OutChar(&Dest, '0');
                        --UsePrecision;
                        --UseWidth;
                    }
                    while ((UsePrecision) && (TempDest.At != Temp)) {
                        OutChar(&Dest, *Temp++);
                        --UsePrecision;
                        --UseWidth;
                    }

                    if (LeftJustify) {
                        while (UseWidth) {
                            OutChar(&Dest, PadWidthZero ? '0' : ' ');
                            --UseWidth;
                        }
                    }
                }
                if (*At) {
                    ++At;
                }

            } else {
                OutChar(&Dest, *At++);
            }
        }
        if (Dest.Size) {
            Dest.At[0] = 0;
        } else {
            Dest.At[-1] = 0;
        }
    }
    uintptr_t Result = Dest.At - DestInit;
    return Result;
}
internal uintptr_t FormatString(uintptr_t DestSize, char *Dest, const char *Format, ...) {
    va_list ArgList;
    va_start(ArgList, Format);
    uintptr_t Result = FormatStringList(DestSize, Dest, Format, ArgList);
    va_end(ArgList);
    return Result;
}

#endif